package com.sneakxy.mybatis.commons.repository.config;

import java.util.Optional;

import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;

import com.sneakxy.mybatis.commons.orm.MybatisEntityManager;
import com.sneakxy.mybatis.commons.repository.support.MybatisEntityInformation;
import com.sneakxy.mybatis.commons.repository.support.MybatisQueryLookupStrategy;

/**
 * @author 潜行的青衣
 */
public class MybatisRepositoryFactoryBean extends RepositoryFactorySupport {

	private SqlSessionFactory sqlSessionFactory;
	
	private SqlSessionTemplate sqlSessionTemplate;
	
	private MybatisCommonsConfiguration configuration;
	
	private Object mapper;
	
	private MybatisEntityManager mybatisEntityManager;
	
	private Class<?> repositoryBaseClass;
	
	@Override
	protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key, QueryMethodEvaluationContextProvider evaluationContextProvider) {
		return Optional.of(new MybatisQueryLookupStrategy(
				mapper, mybatisEntityManager,
				key, evaluationContextProvider));
	}

	/* (non-Javadoc)
	 * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryInformation)
	 */
	@SuppressWarnings("rawtypes")
	@Override
	protected Object getTargetRepository(RepositoryInformation metadata) {
		MybatisEntityInformation entityInformation = (MybatisEntityInformation) this.getEntityInformation(metadata.getDomainType());
		mybatisEntityManager = new MybatisEntityManager(
				metadata, entityInformation, 
				this.sqlSessionTemplate, this.configuration);
		Object repository = this.getTargetRepositoryViaReflection(
				metadata, mybatisEntityManager);
		try {
			if(repository instanceof InitializingBean) {
				((InitializingBean)repository).afterPropertiesSet();
			}
			mapper = this.createMapper(metadata.getRepositoryInterface());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return repository;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	public Object createMapper(Class<?> type) throws Exception {
		MapperFactoryBean mapperFactory = new MapperFactoryBean();
		mapperFactory.setSqlSessionFactory(sqlSessionFactory);
		mapperFactory.setSqlSessionTemplate(sqlSessionTemplate);
		mapperFactory.setMapperInterface(type);
		mapperFactory.afterPropertiesSet();
		return mapperFactory.getObject();
	}
	
	/* (non-Javadoc)
	 * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryBaseClass(org.springframework.data.repository.core.RepositoryMetadata)
	 */
	@Override
	protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
		return repositoryBaseClass;
	}

	public void setRepositoryBaseClass(Class<?> repositoryBaseClass) {
		this.repositoryBaseClass = repositoryBaseClass;
	}

	public SqlSessionFactory getSqlSessionFactory() {
		return sqlSessionFactory;
	}

	public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
		this.sqlSessionFactory = sqlSessionFactory;
	}

	public SqlSessionTemplate getSqlSessionTemplate() {
		return sqlSessionTemplate;
	}

	public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
		this.sqlSessionTemplate = sqlSessionTemplate;
	}

	public Configuration getConfiguration() {
		return configuration;
	}

	public void setConfiguration(MybatisCommonsConfiguration configuration) {
		this.configuration = configuration;
	}

	/* (non-Javadoc)
	 * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class)
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Override
	public <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) {
		return new MybatisEntityInformation(domainClass, configuration.getTypeHandlerRegistry());
	}

}
